package com.lagou.edu.bean;

import com.google.common.base.Strings;
import com.lagou.edu.anno.Autowired;
import com.lagou.edu.utils.JsonUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 默认实现的容器工厂类
 * @author wsz
 * @date 2021/10/20 8:48
 **/
public class DefaultListableBeanFactory implements BeanFactory {
    /**
     * 模拟二层缓存
     */
    private Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    private static Map<String, Object> earlySingletonObjects = new HashMap(16);

    private ProxyFactory proxyFactory;
    private PackageScan scan;

    /**
     * 默认com包
     */
    public DefaultListableBeanFactory() {
        // FIXME 可以读取配置文件
        this("com");
    }

    public DefaultListableBeanFactory(String packageName) {
        scan = new PackageScan(packageName);

        refreshBeanFactory();
        registryBeanDefinition();
        registryBean();
    }

    @Override
    public void refreshBeanFactory() {
        singletonObjects.clear();
        earlySingletonObjects.clear();
    }

    @Override
    public void registryBeanDefinition() {
        scan.initDefinition();
    }

    @Override
    public void registryBean() {
        Set<BeanDefinition> annoClass = scan.getBeanDefinition();
        try {
            for (BeanDefinition beanDefinition: annoClass) {
                // TODO 支持懒加载
                if (!beanDefinition.getLazyInit()) {
                    if (beanDefinition.getSingleton()) {
                        doGetBean(beanDefinition.getId());
                    } else {
                        System.out.println("cannot create none single bean: " + beanDefinition.getId());
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        proxyFactory = (ProxyFactory) getSingleton("ProxyFactory");
    }

    /**
     * 根据BeanDefinitionn直接注册Bean
     * @param beanName
     */
    private Object doGetBean(String beanName) {
        BeanDefinition beanDefinition = scan.getBeanDefinition(beanName);
        if (beanDefinition == null) {
            // FIXME 实现接口的子类，若重新命名，则其他地方注入需指定value
            throw new RuntimeException("cannot find beanDefinition:" + beanName);
        }
        try {
            // 1.实例化+提前暴漏
            Class parentClass = beanDefinition.getTarget();
            String parentName = beanDefinition.getId();
            Object instance = parentClass.newInstance();

            // FIXME 实现的接口也预先进行处理
            Set<String> interfacesNames = beanDefinition.getInterfacesNames();
            if (interfacesNames != null && !interfacesNames.isEmpty()) {
                for (String ifName: interfacesNames) {
                    earlySingletonObjects.put(ifName, instance);
                }
            }
            earlySingletonObjects.put(parentName, instance);
            //  Before
            instance = beanPostProcessorBefore(instance, parentName, "postProcessBeforeInitialization");

            // 2.属性填充
            populateBean(instance);

            // 3.清空二级缓存
            singletonObjects.put(parentName, instance);
            earlySingletonObjects.remove(parentName);
            if (interfacesNames != null && !interfacesNames.isEmpty()) {
                for (String ifName: interfacesNames) {
                    singletonObjects.put(ifName, instance);
                    earlySingletonObjects.remove(ifName);
                }
            }

            //  After
            instance = beanPostProcessorBefore(instance, parentName, "postProcessAfterInitialization");
            return instance;
        } catch (Exception ex) {
            throw new RuntimeException("create bean error:" + beanName);
        }
    }

    private Object beanPostProcessorBefore(Object bean, String beanName, String methodName) {
        Class<?> aClass = bean.getClass();
        boolean assignable = BeanPostProcessor.class.isAssignableFrom(aClass);
        if (!assignable) return bean;
        // 执行钩子函数
        try {
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for(Method m: declaredMethods) {
                if (m.getName().equals(methodName)) {
                    return m.invoke(bean, bean, beanName);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return bean;
    }

    /**
     * 属性填充，存在层级调用/循环依赖
     * @param parent
     * @throws Exception
     */
    private void populateBean(Object parent) throws Exception {
        Field[] declaredFields = parent.getClass().getDeclaredFields();
        for(Field field: declaredFields) {
            // FIXME 只支持 @Autowired
            Autowired annotation = field.getAnnotation(Autowired.class);
            if (annotation == null) continue;

            Class<?> type = field.getType();

            /**
             * 需要被注入的beanName
             * 如果为接口，多个实现类必须value有值
             */
            String beanName = JsonUtils.toCaseStr(Strings.isNullOrEmpty(annotation.value()) ? type.getSimpleName() : annotation.value());
            Object bean = getSingleton(beanName);
            field.setAccessible(true);
            field.set(parent, bean);

            earlySingletonObjects.put(beanName, bean);
            singletonObjects.put(beanName, bean);
        }
    }

    /**
     * 先从一级缓存中获取，再从提前暴漏中获取
     * @param beanName
     * @return
     */
    private Object getSingleton(String beanName) {
        beanName = JsonUtils.toCaseStr(beanName);
        // 一级
        Object bean = singletonObjects.get(beanName);
        if (bean == null) {
            // 二级
            bean = earlySingletonObjects.get(beanName);
            if (bean == null) {
                // 生成
                bean = doGetBean(beanName);
            }
        }
        return bean;
    }

    /**
     * 根据名称获取Bean
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) {
        beanName = JsonUtils.toCaseStr(beanName);
        Object bean = singletonObjects.get(beanName);
        if (bean == null) {
            // TODO 懒加载模式下，需要重新生成Bean
            bean = doGetBean(beanName);
        }
        // TODO 返回被代理对象，内部进行事务管理
        return proxyFactory.getProxy(bean);
    }

    /**
     * 根据class获取Bean
     * @param clazz
     * @return
     */
    @Override
    public <T> T getBean(Class clazz) {
        String simpleName = clazz.getSimpleName();
        return (T) getBean(simpleName);
    }

    static DefaultListableBeanFactory factory;
    public static <T> T getBeanStatic(String beanName) {
        if (factory == null) {
            factory = new DefaultListableBeanFactory();
        }
        return (T) factory.getBean(beanName);
    }

    public static <T> T getBeanStatic(Class clazz) {
        if (factory == null) {
            factory = new DefaultListableBeanFactory();
        }
        return (T) factory.getBean(clazz);
    }
}
